{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "I am following the 3rd Global Edition of **Computer Systems: A Programmer's Perspective** by Bryant and O'Hallaron. IEEE floats are covered in Section 2.4.2\n",
    "\n",
    "IEEE-754 floating-point standard represents a number in a form $V = (-1)^s \\times M \\times 2^E$\n",
    "\n",
    "- sign $s$: 1 is negative, 0 is positive\n",
    "- significand $M$ is a fractional binary number that ranges between 1 and 2-eps (for normalized floats) or between 0 and 1-eps (for denormalized floats)\n",
    "- exponent $E$ weights the value by a (possibly negative) power of 2\n",
    "\n",
    "In particular:\n",
    "- fp32 has 1 sign bit, 8 exponent bits (uint with bias of 127), 23 fraction bits\n",
    "- fp64 has 1 sign bit, 11 exponent bits (uint with bias of 1023), 52 fraction bits\n",
    "\n",
    "Depending on exponent bits the value represented falls into three cases:\n",
    "- **Normalized values** when exp is not all zero or all one. The exponent value is the uint in the exp field, shifted by a bias. The fractional value 0 <= f < 1 is encoded in the frac bits. The significand M is then M = 1 + f (\"implied leading 1 representation\"). This representation is a trick for getting an additional bit of precision for free, since (just as is the case in scientific notation) we can always adjust the exponent E so that the significant M is in the range 1 <= M < 2 (assuming there is no overflow). We therefore do not need to explicitly represent the leading bit, since it will always equal 1.\n",
    "- **Denormalized values** when exp is all zeros, then exponent is \"clamped\" to E = 1 - bias (instead of simply -bias, as it would be if we followed the rules for normalized values), but then the significand M not does not assume the leading bit, so 0 <= M < 1. Since M == 0 is now a possibility we can also represent exact zero.\n",
    "- **Spcial cases** when exp field is all ones. When fraction field is all zeros we get pos/neg infinity depending on sign bit. Otherwise we get nan. (Hey that's a lot of wasted representation space to collapse into nan! :))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "class UInt:\n",
    "    \n",
    "    @classmethod\n",
    "    def val(self, bits: str):\n",
    "        u = sum(int(d) * 2**i for i,d in enumerate(bits[::-1]))\n",
    "        return u\n",
    "\n",
    "class Frac:\n",
    "    \n",
    "    @classmethod\n",
    "    def val(self, bits: str):\n",
    "        u = sum(int(d) * 2**(-i-1) for i,d in enumerate(bits))\n",
    "        return u"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "from fractions import Fraction\n",
    "\n",
    "class Float:\n",
    "    \n",
    "    @classmethod\n",
    "    def val(self, bits: str, verbose=True):\n",
    "        \n",
    "        # the bits are string, with a space between the sign bit, exponent and fraction\n",
    "        sign_bits, exp_bits, frac_bits = bits.split(' ')\n",
    "        assert len(sign_bits) == 1\n",
    "        assert len(exp_bits)  >= 1\n",
    "        assert len(frac_bits) >= 1\n",
    "        k = len(exp_bits)\n",
    "        n = len(frac_bits)\n",
    "        bias = 2**(k - 1) - 1\n",
    "        \n",
    "        # decode the raw binary representations of the exponent and the fraction\n",
    "        sign = int(sign_bits)\n",
    "        e = UInt.val(exp_bits)\n",
    "        f = Frac.val(frac_bits)\n",
    "        # now interpret the \"implied\" exponent / fraction and handle special cases\n",
    "        if e == 2**k - 1:\n",
    "            # all of exponent bits are 1: special-cased infinity, nan, etc.\n",
    "            if f == 0:\n",
    "                # when fraction is all zeros we get infinity\n",
    "                if verbose:\n",
    "                    print(f\"{bits=} {'+' if sign == 0 else '-'}Infinity\")\n",
    "                return float('inf') if sign == 0 else float('-inf')\n",
    "            else:\n",
    "                # otherwise we get nan\n",
    "                if verbose:\n",
    "                    print(f\"{bits=} NaN\")\n",
    "                return float('nan')\n",
    "        elif e > 0:\n",
    "            # a typical normalized float\n",
    "            E = e - bias\n",
    "            M = 1 + f\n",
    "        elif e == 0:\n",
    "            # all of exponent bits are 0: denormalized float\n",
    "            E = 1 - bias # we basically \"cap\" e at min of 1, to create an equal-spaced gradual underflow of IEEE754 float\n",
    "            M = f # there is no leading 1. (allowing us to also create an actual zero)\n",
    "        else:\n",
    "            raise ValueError(\"??? should never make it here\")\n",
    "        \n",
    "        # construct the (Python) float manually\n",
    "        V = (-1)**sign * M * 2**E\n",
    "        \n",
    "        # debugging and such\n",
    "        twoPowE = str(2**E) if E >= 0 else \"1/\" + str(2**-E)\n",
    "        fNum = sum(int(d) * 2**(n-i-1) for i,d in enumerate(frac_bits))\n",
    "        fDen = 2**n\n",
    "        fStr = f'{fNum}/{fDen}'\n",
    "        Mnum = fNum + (0 if e == 0 else fDen) # the 2nd expression implements the \"1 + \" part in construction of M from f\n",
    "        Mden = fDen\n",
    "        MStr = f'{Mnum}/{Mden}'\n",
    "        if verbose:\n",
    "            Vfrac = str(Fraction.from_float(V))\n",
    "            print(f\"{bits=} {e=:2}, {E=:3}, 2^E={twoPowE:5}, f={fStr:5}, M={MStr:5}, V={Vfrac:8} Decimal={V}\")\n",
    "        return V\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "bits='0 0000 000' e= 0, E= -6, 2^E=1/64 , f=0/8  , M=0/8  , V=0        Decimal=0.0\n",
      "bits='0 0000 001' e= 0, E= -6, 2^E=1/64 , f=1/8  , M=1/8  , V=1/512    Decimal=0.001953125\n",
      "bits='0 0000 010' e= 0, E= -6, 2^E=1/64 , f=2/8  , M=2/8  , V=1/256    Decimal=0.00390625\n",
      "bits='0 0000 011' e= 0, E= -6, 2^E=1/64 , f=3/8  , M=3/8  , V=3/512    Decimal=0.005859375\n",
      "...\n",
      "bits='0 0000 111' e= 0, E= -6, 2^E=1/64 , f=7/8  , M=7/8  , V=7/512    Decimal=0.013671875\n",
      "bits='0 0001 000' e= 1, E= -6, 2^E=1/64 , f=0/8  , M=8/8  , V=1/64     Decimal=0.015625\n",
      "bits='0 0001 001' e= 1, E= -6, 2^E=1/64 , f=1/8  , M=9/8  , V=9/512    Decimal=0.017578125\n",
      "...\n",
      "bits='0 0110 110' e= 6, E= -1, 2^E=1/2  , f=6/8  , M=14/8 , V=7/8      Decimal=0.875\n",
      "bits='0 0110 111' e= 6, E= -1, 2^E=1/2  , f=7/8  , M=15/8 , V=15/16    Decimal=0.9375\n",
      "bits='0 0111 000' e= 7, E=  0, 2^E=1    , f=0/8  , M=8/8  , V=1        Decimal=1.0\n",
      "bits='0 0111 001' e= 7, E=  0, 2^E=1    , f=1/8  , M=9/8  , V=9/8      Decimal=1.125\n",
      "bits='0 0111 010' e= 7, E=  0, 2^E=1    , f=2/8  , M=10/8 , V=5/4      Decimal=1.25\n",
      "...\n",
      "bits='0 1110 110' e=14, E=  7, 2^E=128  , f=6/8  , M=14/8 , V=224      Decimal=224.0\n",
      "bits='0 1110 111' e=14, E=  7, 2^E=128  , f=7/8  , M=15/8 , V=240      Decimal=240.0\n",
      "bits='0 1111 000' +Infinity\n",
      "bits='1 1111 000' -Infinity\n",
      "bits='1 1111 111' NaN\n"
     ]
    }
   ],
   "source": [
    "# Reproduce Figure 2.35 (Page 145). Floats with k=4 exponent bits, n=3 fraction bits\n",
    "\n",
    "# Denormalized float values\n",
    "Float.val(\"0 0000 000\") # zero\n",
    "Float.val(\"0 0000 001\") # smallest positive\n",
    "Float.val(\"0 0000 010\")\n",
    "Float.val(\"0 0000 011\")\n",
    "print('...')\n",
    "Float.val(\"0 0000 111\") # largest denormalized\n",
    "# Normalized float values\n",
    "Float.val(\"0 0001 000\") # smallest normalized\n",
    "Float.val(\"0 0001 001\")\n",
    "print('...')\n",
    "Float.val(\"0 0110 110\")\n",
    "Float.val(\"0 0110 111\")\n",
    "Float.val(\"0 0111 000\") # one\n",
    "Float.val(\"0 0111 001\")\n",
    "Float.val(\"0 0111 010\")\n",
    "print('...')\n",
    "Float.val(\"0 1110 110\")\n",
    "Float.val(\"0 1110 111\") # largest normalized\n",
    "# Special values\n",
    "Float.val(\"0 1111 000\") # infinity\n",
    "Float.val(\"1 1111 000\") # infinity\n",
    "Float.val(\"1 1111 111\"); # infinity"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "# we need a bit of tooling for creating inputs to Float\n",
    "\n",
    "from functools import partial\n",
    "\n",
    "def gen_bits(n):\n",
    "    # generate all bit strings of length n in increasing order\n",
    "    for i in range(2**n):\n",
    "        b = str(bin(i))[2:]\n",
    "        b = '0' * (n - len(b)) + b\n",
    "        yield b\n",
    "\n",
    "def chunk_str3(s, parts):\n",
    "    assert sum(parts) == len(s)\n",
    "    return s[:parts[0]] + ' ' + s[parts[0]:parts[0]+parts[1]] + ' ' + s[parts[0]+parts[1]:]\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "bits='0 000 00' e= 0, E= -2, 2^E=1/4  , f=0/4  , M=0/4  , V=0        Decimal=0.0\n",
      "bits='0 000 01' e= 0, E= -2, 2^E=1/4  , f=1/4  , M=1/4  , V=1/16     Decimal=0.0625\n",
      "bits='0 000 10' e= 0, E= -2, 2^E=1/4  , f=2/4  , M=2/4  , V=1/8      Decimal=0.125\n",
      "bits='0 000 11' e= 0, E= -2, 2^E=1/4  , f=3/4  , M=3/4  , V=3/16     Decimal=0.1875\n",
      "bits='0 001 00' e= 1, E= -2, 2^E=1/4  , f=0/4  , M=4/4  , V=1/4      Decimal=0.25\n",
      "bits='0 001 01' e= 1, E= -2, 2^E=1/4  , f=1/4  , M=5/4  , V=5/16     Decimal=0.3125\n",
      "bits='0 001 10' e= 1, E= -2, 2^E=1/4  , f=2/4  , M=6/4  , V=3/8      Decimal=0.375\n",
      "bits='0 001 11' e= 1, E= -2, 2^E=1/4  , f=3/4  , M=7/4  , V=7/16     Decimal=0.4375\n",
      "bits='0 010 00' e= 2, E= -1, 2^E=1/2  , f=0/4  , M=4/4  , V=1/2      Decimal=0.5\n",
      "bits='0 010 01' e= 2, E= -1, 2^E=1/2  , f=1/4  , M=5/4  , V=5/8      Decimal=0.625\n",
      "bits='0 010 10' e= 2, E= -1, 2^E=1/2  , f=2/4  , M=6/4  , V=3/4      Decimal=0.75\n",
      "bits='0 010 11' e= 2, E= -1, 2^E=1/2  , f=3/4  , M=7/4  , V=7/8      Decimal=0.875\n",
      "bits='0 011 00' e= 3, E=  0, 2^E=1    , f=0/4  , M=4/4  , V=1        Decimal=1.0\n",
      "bits='0 011 01' e= 3, E=  0, 2^E=1    , f=1/4  , M=5/4  , V=5/4      Decimal=1.25\n",
      "bits='0 011 10' e= 3, E=  0, 2^E=1    , f=2/4  , M=6/4  , V=3/2      Decimal=1.5\n",
      "bits='0 011 11' e= 3, E=  0, 2^E=1    , f=3/4  , M=7/4  , V=7/4      Decimal=1.75\n",
      "bits='0 100 00' e= 4, E=  1, 2^E=2    , f=0/4  , M=4/4  , V=2        Decimal=2.0\n",
      "bits='0 100 01' e= 4, E=  1, 2^E=2    , f=1/4  , M=5/4  , V=5/2      Decimal=2.5\n",
      "bits='0 100 10' e= 4, E=  1, 2^E=2    , f=2/4  , M=6/4  , V=3        Decimal=3.0\n",
      "bits='0 100 11' e= 4, E=  1, 2^E=2    , f=3/4  , M=7/4  , V=7/2      Decimal=3.5\n",
      "bits='0 101 00' e= 5, E=  2, 2^E=4    , f=0/4  , M=4/4  , V=4        Decimal=4.0\n",
      "bits='0 101 01' e= 5, E=  2, 2^E=4    , f=1/4  , M=5/4  , V=5        Decimal=5.0\n",
      "bits='0 101 10' e= 5, E=  2, 2^E=4    , f=2/4  , M=6/4  , V=6        Decimal=6.0\n",
      "bits='0 101 11' e= 5, E=  2, 2^E=4    , f=3/4  , M=7/4  , V=7        Decimal=7.0\n",
      "bits='0 110 00' e= 6, E=  3, 2^E=8    , f=0/4  , M=4/4  , V=8        Decimal=8.0\n",
      "bits='0 110 01' e= 6, E=  3, 2^E=8    , f=1/4  , M=5/4  , V=10       Decimal=10.0\n",
      "bits='0 110 10' e= 6, E=  3, 2^E=8    , f=2/4  , M=6/4  , V=12       Decimal=12.0\n",
      "bits='0 110 11' e= 6, E=  3, 2^E=8    , f=3/4  , M=7/4  , V=14       Decimal=14.0\n",
      "bits='0 111 00' +Infinity\n",
      "bits='0 111 01' NaN\n",
      "bits='0 111 10' NaN\n",
      "bits='0 111 11' NaN\n"
     ]
    }
   ],
   "source": [
    "# Reproduce Figure 2.34, a scatter plot of a float range, for k=3 exponent bits and n=2 fraction bits\n",
    "\n",
    "import matplotlib\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline  \n",
    "\n",
    "vs = []\n",
    "chunk = partial(chunk_str3, parts=(1, 3, 2))\n",
    "for bstr in map(chunk, gen_bits(6)):\n",
    "    V = Float.val(bstr, verbose=bstr[0]=='0')\n",
    "    vs.append(V)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Observation**: Sorted binary representations map to sorted floats. This is because M is always 0 < M < 2, while bumping the exponent bit by 1 produces a 2X jump."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 1440x144 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# plot all the achieved values, full range of representation (except infs and nans ofc)\n",
    "plt.figure(figsize=(20, 2))\n",
    "plt.scatter(vs, [0]*len(vs), s=40)\n",
    "plt.grid()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAABKEAAACMCAYAAACkurrcAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8vihELAAAACXBIWXMAAAsTAAALEwEAmpwYAAAYc0lEQVR4nO3df5AcZ3ng8e8jybKJZE52LMvCFrEoC3HiQpxADCnnhyhDIqmSyNzFnJwrowBXwhfrLlTBVQzUcdSpuHKRc0hIbKuEozpRRSJcAWOVT8QYVTYO5BwkO45t+UcshIOEhBQMZ7OyWbHWc39ML7R3Z3d7mOmd3tnvp2pqprvft/fZ932qp/WouzcyE0mSJEmSJKlO8/odgCRJkiRJkgafRShJkiRJkiTVziKUJEmSJEmSamcRSpIkSZIkSbWzCCVJkiRJkqTaWYSSJEmSJElS7Rb0O4BeWrJkSV522WX9DkOzxKlTp1i0aFG/w9AsYK6oE+aLqjJX1AnzRVWZK+qE+aKqHnjggW9n5tJu99OTIlRErAP+GJgP3J6ZN43bHsX2DcDzwO9k5oPFtqeB7wEvAqOZ+YZi/fnAZ4BLgaeBt2fmd6eKY9myZRw4cKAXv5LmgKGhIdauXdvvMDQLmCvqhPmiqswVdcJ8UVXmijphvqiqiPjnXuyn69vxImI+cAuwHlgDXBsRa8Y1Ww+sKl5bgNvGbX9zZl4+VoAq3Ajsy8xVwL5iWZIkSZIkSbNQL54JdQVwKDMPZ+ZpYDewcVybjcCnsuV+YElELJ9mvxuBXcXnXcDVPYhVkiRJkiRJfdCLItTFwJHS8tFiXdU2CXwxIh6IiC2lNssy8zhA8X5hD2KVJEmSJElSH/TimVDRZl120ObKzDwWERcC90bEE5l5X+Uf3ipcbQFYunQpQ0NDVbtqjhseHjZfVIm5ok6YL6rKXFEnzBdVZa6oE+aLZlovilBHgRWl5UuAY1XbZObY+8mIuJPW7X33ASciYnlmHi9u3TvZ7odn5g5gB8Dq1avTh6qpKh/Cp6rMFXXCfFFV5oo6Yb6oKnNFnTBfNNN6cTvefmBVRKyMiIXAJmDPuDZ7gHdEy5uAZ4vi0qKIOBcgIhYBvwo8Wuqzufi8GbirB7FKkiRJkiSpD7q+EiozRyNiK3APMB/YmZkHI+L6Yvt2YC+wATgEPA+8s+i+DLgzIsZi+fPM/Kti203AHRHxbuAbwDXdxipJkiRJkqT+6MXteGTmXlqFpvK67aXPCdzQpt9h4Gcm2eczwFW9iE+SJEmSJEn91Yvb8SRJkiRJkqQpWYSSJEmSJElS7SxCSZIkSZIkqXYWoSRJkiRJklQ7i1CSJEmSJEmqnUUoSZIkSZIk1c4ilCRJkiRJkmpnEUqSJEmSJEm1swglSZIkSZKk2lmEkiRJkiRJUu0sQkmSJEmSJKl2FqEkSZIkSZJUO4tQkiRJkiRJqp1FKEmSJEmSJNXOIpQkSZIkSZJqZxFKkiRJkiRJtetJESoi1kXEkxFxKCJubLM9IuITxfaHI+LnivUrIuKvI+LxiDgYEb9X6vORiPhmRDxUvDb0IlZJkiRJkiTNvAXd7iAi5gO3AG8FjgL7I2JPZj5WarYeWFW83gjcVryPAu/LzAcj4lzggYi4t9T345n5v7qNUZIkSZIkSf3ViyuhrgAOZebhzDwN7AY2jmuzEfhUttwPLImI5Zl5PDMfBMjM7wGPAxf3ICZJkiRJkiQ1SC+KUBcDR0rLR5lYSJq2TURcCvws8Pel1VuL2/d2RsR5PYhVkiRJkiRJfdD17XhAtFmXnbSJiMXAZ4H3ZuZzxerbgG1Fu23AzcC7JvzwiC3AFoClS5cyNDTUYfiaq4aHh80XVWKuqBPmi6oyV9QJ80VVmSvqhPmimdaLItRRYEVp+RLgWNU2EXEWrQLUpzPzc2MNMvPE2OeI+CRwd7sfnpk7gB0Aq1evzrVr1/64v4fmmKGhIcwXVWGuqBPmi6oyV9QJ80VVmSvqhPmimdaL2/H2A6siYmVELAQ2AXvGtdkDvKP4K3lvAp7NzOMREcCfAY9n5h+WO0TE8tLi24BHexCrJEmSJEmS+qDrK6EyczQitgL3APOBnZl5MCKuL7ZvB/YCG4BDwPPAO4vuVwLXAY9ExEPFug9m5l7gYxFxOa3b8Z4G3tNtrJIkSZIkSeqPXtyOR1E02jtu3fbS5wRuaNPvy7R/XhSZeV0vYpMkSZIkSVL/9eJ2PEmSJEmSJGlKFqEkSZIkSZJUO4tQkiRJkiRJqp1FKEmSJEmSJNXOIpQkSZIkSZJqZxFKkiRJkiRJtbMIJUmSJEmSpNpZhJIkSZIkSVLtLEJJkiRJkiSpdhahJEmSJEmSVDuLUJIkSZIkSaqdRShJkiRJkiTVziKUJEmSJEmSamcRSpIkSZIkSbWzCCVJkiRJkqTaDVQRKhNOjYz2O4xGOTUyyuF/GXZcxjk1Msrp0TOOyzjmy0RNypUmzY+xTB6L+dLcWJoSB8DJ577P8MgoJ5/7fr9DadS4GMvksXhsaW4sTYljLJam5EqTNGmOmsR8ac98mejUyCixYOHZvdhXZGb3O4lYB/wxMB+4PTNvGrc9iu0bgOeB38nMB6fqGxHnA58BLgWeBt6emd+dKo5XvuqyfNlv/wmbfn4F/+3X17Bg/kDV2Doy+uIZtt39GLv3H2HBvGD0TDouvHRc/suaH/CJx85yXDBf2mlSrjRpfoxl+ljMl+bF0pQ4AL5/epSrb/0KT3xrmPf99Cg3P7KA11y0mM//7pWcs3DBjMbSpHExlulj8djSvFiaEsf4WPqdK03SpDlqEvOlPfNlovKYPH371jMj3/ra/G732fVIRsR84BZgPbAGuDYi1oxrth5YVby2ALdV6HsjsC8zVwH7iuVpjYye4Y4DR9h292Nd/V6z3ba7H+OOA0cYGT3DqdMvOi6F8ricyXRcCubLRE3KlSbNj7FMH4v50rxYmhIH8MMCVNkT3xrm6lu/MuOxNGlcjGX6WDy2NC+WpsQxPpZ+50qTNGmOmsR8ac98mag8JsS8nlTierGTK4BDmXk4M08Du4GN49psBD6VLfcDSyJi+TR9NwK7is+7gKurBvTCD87wmQNH5uzlc6dGRtm9/wgv/ODMS9Y7Lo5LO47LRE0aE2MxFmMZjDigdQve+ALUmCe+NTyjt+Y1aVyMxVhmYyxNiaNpsTSJ49Ke49Ke4zLRZGPSra5vx4uI3wLWZeZ/LJavA96YmVtLbe4GbsrMLxfL+4Dfp3WrXdu+EfH/MnNJaR/fzczz2vz8LbSuruKCC5a+/sN/9EkA5kWw6sLFLFww9y6bOz16hqdODnOmzdw6Lj8al2UvgxMvtLY5LuZLWZNypUnzYyzVYjFfmhVLU+IAGB4Z5evfPvXD5XKuAKy8YBGLz56ZW/KaNC7GUi0Wjy3NiqUpcbSLxXPclibNUZOYL+2ZLxONH5P3v//9jBx/Krrdby/OdNoFMX7mJmtTpe+UMnMHsANaz4S6+ZHWr3TOWfN44N/9Motm6GSuSU6NjPKft93bumRuHMflR+My9iwOcFzMl5dqUq40aX6MpVos5kuzYmlKHNC6Emrr/9z3w+VyrgB89YO/yIUvP2dGYmnSuBhLtVg8tjQrlqbE0S4Wz3FbmjRHTWK+tGe+TDTVmHSjF6W8o8CK0vIlwLGKbabqe6K4ZY/i/WTVgF521jz+/RtWzLkkGbPo7AVs+vkVvOysl06v4+K4tOO4TNSkMTEWYzGWwYgD4MKXn8NrLlrcdttrLlo8YwUoaNa4GIuxzMZYmhJH02JpEselPcelPcdlosnGpFu9GMn9wKqIWAl8E9gE/Pa4NnuArRGxG3gj8GxmHo+If5mi7x5gM3BT8X5XlWDOOWseb39D6wn2c9nY7/+ZA0eYH8GLmY4LLx2XeRHmS8F8mahJudKk+TGW6WMxX5oXS1PiAPj871454eHkY38db6Y1aVyMZfpYPLY0L5amxDE+ln7nSpM0aY6axHxpz3yZqDwm5JmeXBLV9TOhACJiA/BHwHxgZ2Z+NCKuB8jM7RERwJ8C64DngXdm5oHJ+hbrfxK4A3gl8A3gmsz8zlRxvPrVq/MfHjk4J6uUkzk1MsqJ577Pspef47iUnBoZ5St/ex9X/tLcu6xyKubLRE3KlSbNj7FMHov50txYmhIHtG7N++r//TJX/MLM3YI3mSaNi7FMHovHlubG0pQ4xmJpSq40SZPmqEnMl/bMl4lOjYxy7uJFj575wchPd7uvnoxoZu4F9o5bt730OYEbqvYt1j8DXNVJHBGYJOMsOnsBr1ra/tL/uWzR2QtYuGCe+TKO+TJRk3KlSfNjLO2ZL+01JZamxAGtW/MWn72g7wUoaNa4GEt7Hlvaa0osTYkDmpUrTdKkOWoS86U982WiRWcvIEdPj/RiX3Pr8e6SJEmSJEnqC4tQkiRJkiRJqp1FKEmSJEmSJNXOIpQkSZIkSZJqZxFKkiRJkiRJtbMIJUmSJEmSpNpZhJIkSZIkSVLtLEJJkiRJkiSpdhahJEmSJEmSVDuLUJIkSZIkSaqdRShJkiRJkiTVziKUJEmSJEmSamcRSpIkSZIkSbWzCCVJkiRJkqTaWYSSJEmSJElS7boqQkXE+RFxb0Q8VbyfN0m7dRHxZEQciogbS+v/ICKeiIiHI+LOiFhSrL80Il6IiIeK1/Zu4pQkSZIkSVJ/dXsl1I3AvsxcBewrll8iIuYDtwDrgTXAtRGxpth8L/BvMvN1wD8BHyh1/VpmXl68ru8yTkmSJEmSJPVRt0WojcCu4vMu4Oo2ba4ADmXm4cw8Dewu+pGZX8zM0aLd/cAlXcYjSZIkSZKkBuq2CLUsM48DFO8XtmlzMXCktHy0WDfeu4AvlJZXRsQ/RMTfRMQvdRmnJEmSJEmS+mjBdA0i4kvARW02fajiz4g263Lcz/gQMAp8ulh1HHhlZj4TEa8HPh8Rr83M59rEtwXYArB06VKGhoYqhqW5bnh42HxRJeaKOmG+qCpzRZ0wX1SVuaJOmC+aadMWoTLzLZNti4gTEbE8M49HxHLgZJtmR4EVpeVLgGOlfWwGfh24KjOz+JkjwEjx+YGI+BrwauBAm/h2ADsAVq9enWvXrp3uV5IAGBoawnxRFeaKOmG+qCpzRZ0wX1SVuaJOmC+aad3ejrcH2Fx83gzc1abNfmBVRKyMiIXApqIfEbEO+H3gNzPz+bEOEbG0eKA5EfEqYBVwuMtYJUmSJEmS1CfdFqFuAt4aEU8Bby2WiYhXRMRegOLB41uBe4DHgTsy82DR/0+Bc4F7I+KhiNherP9l4OGI+EfgL4HrM/M7XcYqSZIkSZKkPpn2drypZOYzwFVt1h8DNpSW9wJ727S7bJL9fhb4bDexSZIkSZIkqTm6vRJKkiRJkiRJmpZFKEmSJEmSJNXOIpQkSZIkSZJqZxFKkiRJkiRJtbMIJUmSJEmSpNpZhJIkSZIkSVLtLEJJkiRJkiSpdhahJEmSJEmSVDuLUJIkSZIkSaqdRShJkiRJkiTVziKUJEmSJEmSamcRSpIkSZIkSbWzCCVJkiRJkqTaWYSSJEmSJElS7SxCSZIkSZIkqXYWoSRJkiRJklS7ropQEXF+RNwbEU8V7+dN0m5dRDwZEYci4sbS+o9ExDcj4qHitaG07QNF+ycj4te6iVOSJEmSJEn91e2VUDcC+zJzFbCvWH6JiJgP3AKsB9YA10bEmlKTj2fm5cVrb9FnDbAJeC2wDri12I8kSZIkSZJmoW6LUBuBXcXnXcDVbdpcARzKzMOZeRrYXfSbbr+7M3MkM78OHCr2I0mSJEmSpFmo2yLUssw8DlC8X9imzcXAkdLy0WLdmK0R8XBE7CzdzjddH0mSJEmSJM0iC6ZrEBFfAi5qs+lDFX9GtFmXxfttwLZieRtwM/CuafqMj28LsAVg6dKlDA0NVQxLc93w8LD5okrMFXXCfFFV5oo6Yb6oKnNFnTBfNNOmLUJl5lsm2xYRJyJieWYej4jlwMk2zY4CK0rLlwDHin2fKO3rk8Dd0/VpE98OYAfA6tWrc+3atdP9ShIAQ0NDmC+qwlxRJ8wXVWWuqBPmi6oyV9QJ80Uzrdvb8fYAm4vPm4G72rTZD6yKiJURsZDWA8f3ABSFqzFvAx4t7XdTRJwdESuBVcBXu4xVkiRJkiRJfTLtlVDTuAm4IyLeDXwDuAYgIl4B3J6ZGzJzNCK2AvcA84GdmXmw6P+xiLic1q12TwPvAcjMgxFxB/AYMArckJkvdhmrJEmSJEmS+qSrIlRmPgNc1Wb9MWBDaXkvsLdNu+um2PdHgY92E58kSZIkSZKaITLbPu97VoqI7wFP9jsOzRoXAN/udxCaFcwVdcJ8UVXmijphvqgqc0WdMF9U1erMPLfbnXR7O17TPJmZb+h3EJodIuKA+aIqzBV1wnxRVeaKOmG+qCpzRZ0wX1RVRBzoxX66fTC5JEmSJEmSNC2LUJIkSZIkSardoBWhdvQ7AM0q5ouqMlfUCfNFVZkr6oT5oqrMFXXCfFFVPcmVgXowuSRJkiRJkppp0K6EkiRJkiRJUgPNuiJURFwTEQcj4kxETPoU/4hYFxFPRsShiLixtP78iLg3Ip4q3s+bmcjVD1XmOyJWR8RDpddzEfHeYttHIuKbpW0bZvyX0IyoemyIiKcj4pEiHw502l+DoeKxZUVE/HVEPF58b/1eaZvHlgE32XlIaXtExCeK7Q9HxM9V7avBUiFX/kORIw9HxN9FxM+UtrX9TtLgqpAvayPi2dL3y4er9tVgqZAr/7WUJ49GxIsRcX6xzWPLHBIROyPiZEQ8Osn2np6zzLoiFPAo8G+B+yZrEBHzgVuA9cAa4NqIWFNsvhHYl5mrgH3FsgbXtPOdmU9m5uWZeTnweuB54M5Sk4+Pbc/MvTMRtPqik2PDm4t8KBfCPbbMLVXmexR4X2b+a+BNwA2l7yLw2DKwpjkPGbMeWFW8tgC3ddBXA6LifH8d+JXMfB2wjYnP5Gj3naQB1MHx4W9L3y//o8O+GgBV5jsz/6D0b6APAH+Tmd8pNfHYMnf8b2DdFNt7es4y64pQmfl4Zj45TbMrgEOZeTgzTwO7gY3Fto3AruLzLuDqWgJVU3Q631cBX8vMf64zKDVSt8cGjy1zy7TznZnHM/PB4vP3gMeBi2cqQPXVVOchYzYCn8qW+4ElEbG8Yl8NjmnnOzP/LjO/WyzeD1wywzGqObo5PnhsmVs6ne9rgb+YkcjUOJl5H/CdKZr09Jxl1hWhKroYOFJaPsqPTvyXZeZxaP0DAbhwhmPTzOp0vjcx8QC8tbjscKe3WA20qrmSwBcj4oGI2PJj9Ndg6Gi+I+JS4GeBvy+t9tgyuKY6D5muTZW+Ghydzve7gS+Ulif7TtJgqpovvxAR/xgRX4iI13bYV4Oh8nxHxE/Qugrms6XVHltU1tNzlgU9Da1HIuJLwEVtNn0oM++qsos26/wzgANqqnzpcD8Lgd+kdTnqmNtoXfqexfvNwLt+vEjVbz3KlSsz81hEXAjcGxFPFP97oAHTw2PLYlondu/NzOeK1R5bBluV85DJ2ngOM7dUnu+IeDOtItQvllb7nTS3VMmXB4Gfyszh4nmDn6d1C43Hlrmlk/n+DeAr427F89iisp6eszSyCJWZb+lyF0eBFaXlS4BjxecTEbE8M48Xl5Cd7PJnqc+mypeI6GS+1wMPZuaJ0r5/+DkiPgnc3YuY1R+9yJXMPFa8n4yIO2ldhnofHlsGTi/yJSLOolWA+nRmfq60b48tg22q85Dp2iys0FeDo0quEBGvA24H1mfmM2Prp/hO0mCaNl9K/9lBZu6NiFsj4oIqfTVQOpnvCXeCeGzROD09ZxnU2/H2A6siYmVxdcsmYE+xbQ+wufi8GahyZZVmr07me8K90MU/Lse8jdaD8TWYps2ViFgUEeeOfQZ+lR/lhMeWuaVKvgTwZ8DjmfmH47Z5bBlsU52HjNkDvKP4izNvAp4tbu2s0leDY9r5johXAp8DrsvMfyqtn+o7SYOpSr5cVHz/EBFX0Pr33jNV+mqgVJrviPhXwK9QOo/x2KI2enrO0sgroaYSEW8D/gRYCvyfiHgoM38tIl4B3J6ZGzJzNCK2AvcA84GdmXmw2MVNwB0R8W7gG8A1ffg1NHPaznc5X4rlnwDeCrxnXP+PRcTltC4rfLrNdg2OKrmyDLizOLdbAPx5Zv7VVP01sKrky5XAdcAjEfFQ0e+DxV/C89gywCY7D4mI64vt24G9wAbgEK2/yvrOqfr24dfQDKiYKx8GfhK4tfj+GS3+WtVU30kaQBXz5beA/xQRo8ALwKbMTMBjyxxSMVeg9R9hX8zMU6XuHlvmmIj4C2AtcEFEHAX+O3AW1HPOEq1jkiRJkiRJklSfQb0dT5IkSZIkSQ1iEUqSJEmSJEm1swglSZIkSZKk2lmEkiRJkiRJUu0sQkmSJEmSJKl2FqEkSZIkSZJUO4tQkiRJkiRJqp1FKEmSJEmSJNXu/wMRV3U2Q3dcsQAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<Figure size 1440x144 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# zoom in on range -1..1, expecting to see uniform spacing near zero via denormalized \"gradual underflow\"\n",
    "plt.figure(figsize=(20, 2))\n",
    "plt.scatter(vs, [0]*len(vs), s=40)\n",
    "plt.xlim(-1, 1)\n",
    "plt.grid()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "bits='0 00 00' e= 0, E=  0, 2^E=1    , f=0/4  , M=0/4  , V=0        Decimal=0.0\n",
      "bits='0 00 01' e= 0, E=  0, 2^E=1    , f=1/4  , M=1/4  , V=1/4      Decimal=0.25\n",
      "bits='0 00 10' e= 0, E=  0, 2^E=1    , f=2/4  , M=2/4  , V=1/2      Decimal=0.5\n",
      "bits='0 00 11' e= 0, E=  0, 2^E=1    , f=3/4  , M=3/4  , V=3/4      Decimal=0.75\n",
      "bits='0 01 00' e= 1, E=  0, 2^E=1    , f=0/4  , M=4/4  , V=1        Decimal=1.0\n",
      "bits='0 01 01' e= 1, E=  0, 2^E=1    , f=1/4  , M=5/4  , V=5/4      Decimal=1.25\n",
      "bits='0 01 10' e= 1, E=  0, 2^E=1    , f=2/4  , M=6/4  , V=3/2      Decimal=1.5\n",
      "bits='0 01 11' e= 1, E=  0, 2^E=1    , f=3/4  , M=7/4  , V=7/4      Decimal=1.75\n",
      "bits='0 10 00' e= 2, E=  1, 2^E=2    , f=0/4  , M=4/4  , V=2        Decimal=2.0\n",
      "bits='0 10 01' e= 2, E=  1, 2^E=2    , f=1/4  , M=5/4  , V=5/2      Decimal=2.5\n",
      "bits='0 10 10' e= 2, E=  1, 2^E=2    , f=2/4  , M=6/4  , V=3        Decimal=3.0\n",
      "bits='0 10 11' e= 2, E=  1, 2^E=2    , f=3/4  , M=7/4  , V=7/2      Decimal=3.5\n",
      "bits='0 11 00' +Infinity\n",
      "bits='0 11 01' NaN\n",
      "bits='0 11 10' NaN\n",
      "bits='0 11 11' NaN\n"
     ]
    }
   ],
   "source": [
    "# Now Practice Problem 2.47: 5-bit float with k=2, n=2\n",
    "\n",
    "chunk = partial(chunk_str3, parts=(1, 2, 2))\n",
    "for bstr in map(chunk, gen_bits(5)):\n",
    "    Float.val(bstr, verbose=bstr[0]=='0')\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "bits='0 10001100 10000001110010000000000' e=140, E= 13, 2^E=8192 , f=4252672/8388608, M=12641280/8388608, V=12345    Decimal=12345.0\n",
      "12345.0\n"
     ]
    }
   ],
   "source": [
    "# fp32 example given in the book, verify it gives the integer 12,345\n",
    "book_bits = '0 10001100 10000001110010000000000'\n",
    "V = Float.val(book_bits)\n",
    "print(V)\n",
    "assert V == 12345.0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0 10001100 10000001110010000000000\n"
     ]
    }
   ],
   "source": [
    "# now, as suggested in the book, let's go backwards: take the integer 12,345 and get the fp32 bit pattern\n",
    "\n",
    "def bits(n: int):\n",
    "    bits = []\n",
    "    while n:\n",
    "        bits.append(n % 2)\n",
    "        n = n // 2\n",
    "    return ''.join(map(str, bits[::-1]))\n",
    "\n",
    "num = 12345\n",
    "bitstr = bits(num) # 11000000111001\n",
    "nshift = len(bitstr) - 1 # number of times we have to shift to normalize the number into scientific notation\n",
    "frac_bits = bitstr[1:] + '0'*(23 - nshift) # drop the leading 1, and pad to 23 bits of mantissa in fp32\n",
    "sign_bits = '0' if num >= 0 else '1'\n",
    "ebitstr = bits(127 + nshift) # 127 is the fp32 bias. since we have 8 exp bits, we have 2**(8-1)-1 = 127\n",
    "exp_bits = '0' * (8 - len(ebitstr)) + ebitstr # encode the exponent, with padding of zeros as needed in front\n",
    "bits = ' '.join((sign_bits, exp_bits, frac_bits))\n",
    "print(bits)\n",
    "assert bits == book_bits"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0 10001100 10000001110010000000000\n"
     ]
    }
   ],
   "source": [
    "# finally let's also check that Python / struct agree\n",
    "import struct\n",
    "\n",
    "def binary(num):\n",
    "    return ''.join('{:0>8b}'.format(c) for c in struct.pack('!f', num))\n",
    "\n",
    "py_bits = chunk_str3(binary(12345), (1, 8, 23))\n",
    "print(py_bits)\n",
    "assert py_bits == book_bits"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
